#!/usr/bin/python3
import json
import time
import hashlib
import os
import binascii
from flask import Flask, jsonify, request

app = Flask(__name__)


class Blockchain:

    def __init__(self):
        self.chain = []
        self.difficulty_target = 4
        self.mine_block('Genesis block!')


    def hash_block_header(self, block):
        hashId = hashlib.sha256()
        hashId.update(repr(block['header']).encode('utf-8'))
        return str(hashId.hexdigest())


    def get_last_block(self):
        return self.chain[-1]


    def create_block(self, data = None):

        block = {
            'header' : {
                'block_number': len(self.chain),
                'block_time': int(time.time()),
                'block_nonce': None,
                'previous_block_hash': (None if len(self.chain) == 0 else self.get_last_block()['hash']),
            },
            'data' : data,
            'hash' : None
        }

        return block


    def mine_block(self, data):

        block = self.create_block(data)

        while True:
            block['header']['block_nonce'] = str(binascii.b2a_hex(os.urandom(8)))
            block['hash'] = self.hash_block_header(block)

            if block['hash'][:self.difficulty_target] == '0' * self.difficulty_target:
                break

        self.chain.append(block)

        return block


    def check_chain(self):

        for block_number in reversed(range(len(self.chain))):

            current_block = self.chain[block_number]

            if not current_block['hash'] == self.hash_block_header(current_block):
                return False

            if block_number > 0 and not current_block['header']['previous_block_hash'] == self.chain[block_number - 1]['hash']:
                return False

        return True
      

@app.route('/mine_block', methods = ['GET'])
def mine_block():

    block_data = request.args.get('data', default = '', type = str)
    block = blockchain.mine_block(block_data)
    return jsonify(block), 200


@app.route('/check_blockchain', methods = ['GET'])
def check_blockchain():

    if blockchain.check_chain:
        response = jsonify({'Result': 'OK'})
    else:
        response = jsonify({'Result': 'Invalid'})
    return response, 200


@app.route('/get_blocks', methods = ['GET'])
def get_blocks():

    response = []
    for block in blockchain.chain:
        response.append(block)
    return jsonify(response), 200


@app.route('/get_block', methods = ['GET'])
def get_block():

    try:
        block_number = request.args.get('number', default = 0, type = int)
        block = blockchain.chain[block_number]
    except:
        return jsonify({'Error': 'Invalid block number!'}), 400

    return jsonify(block), 200


blockchain = Blockchain()
app.run(host = '0.0.0.0', port = 8080)
